/*
*
集团里有 n 名员工，他们可以完成各种各样的工作创造利润。

第 i 种工作会产生 profit[i] 的利润，它要求 group[i] 名成员共同参与。如果成员参与了其中一项工作，就不能参与另一项工作。

工作的任何至少产生 minProfit 利润的子集称为 盈利计划 。并且工作的成员总数最多为 n 。

有多少种计划可以选择？因为答案很大，所以 返回结果模 10^9 + 7 的值。

示例 1：

输入：n = 5, minProfit = 3, group = [2,2], profit = [2,3]
输出：2
解释：至少产生 3 的利润，该集团可以完成工作 0 和工作 1 ，或仅完成工作 1 。
总的来说，有两种计划。
示例 2：

输入：n = 10, minProfit = 5, group = [2,3,5], profit = [6,7,8]
输出：7
解释：至少产生 5 的利润，只要完成其中一种工作就行，所以该集团可以完成任何工作。
有 7 种可能的计划：(0)，(1)，(2)，(0,1)，(0,2)，(1,2)，以及 (0,1,2) 。

提示：

1 <= n <= 100
0 <= minProfit <= 100
1 <= group.length <= 100
1 <= group[i] <= 100
profit.length == group.length
0 <= profit[i] <= 100

  - @author ala
  - @date 2024-10-11 14:27
*/
package main

import "fmt"

func main() {
	n, minProfit := 5, 3
	group := []int{2, 2}
	profit := []int{2, 3}

	fmt.Println(profitableSchemes(n, minProfit, group, profit))
}

func profitableSchemes(n int, minProfit int, group []int, profit []int) int {
	return V1(n, minProfit, group, profit)
}

var (
	MOD int = 1e9 + 7
)

/**
 *  1）dp[i][j][k]表示前i个项目，用j个人，至少获利k的方案数
 *      如果第i个项目人头数不够，只能继承 i - 1
 *          dp[i][j][k] = dp[i - 1][j][k]
 *      如果第i个项目人头数够，可做可不做
 *          不做：dp[i][j][k] = dp[i - 1][j][k]
 *          做：dp[i][j][k] = dp[i - 1][j][k] = dp[i - 1][j - g[i]][max(0, k - p[i])]
 *  2）最后dp[N][j][minProfile]合起来就是答案
 */
func V1(n int, minProfit int, group []int, profit []int) int {
	N := len(group)

	dp := make([][][]int, N+1)
	for i := range dp {
		dp[i] = make([][]int, n+1)
		for j := range dp[i] {
			dp[i][j] = make([]int, minProfit+1)
		}
	}
	dp[0][0][0] = 1

	for i := 1; i <= N; i++ {
		g, p := group[i-1], profit[i-1]
		for j := 0; j <= n; j++ {
			for k := 0; k <= minProfit; k++ {
				dp[i][j][k] = dp[i-1][j][k]
				if j >= g {
					dp[i][j][k] = (dp[i][j][k] + dp[i-1][j-g][max(0, k-p)]) % MOD
				}
			}
		}
	}
	res := 0
	for j := 0; j <= n; j++ {
		res = (res + dp[N][j][minProfit]) % MOD
	}
	return res
}
